package org.wen.soft.generate.model;

import lombok.Data;
import org.wen.soft.asm.handler.DescriptorType;
import org.wen.soft.asm.handler.SignatureType;
import org.wen.soft.asm.model.ILodeMethod;
import org.wen.soft.asm.model.LoadInvokeMethod;
import org.wen.soft.asm.model.LoadMethod;
import org.wen.soft.asm.model.LoadParameter;
import org.wen.soft.generate.util.ExcludeUtil;
import org.wen.soft.generate.util.GStrUtil;
import org.wen.soft.generate.util.MappingUtil;
import org.wen.soft.generate.util.ParameterUtil;
import org.wen.soft.util.StrCastUtil;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

@Data
public class GeneMethod {
    int modifier;
    String name;
    int index;                          // 方法的编号
    LoadMethod loadMethod;              // ASM读取的测试方法信息
    LoadInvokeMethod loadInvokeMethod;  // ASM读取的依赖方法信息

    // owner
    String ownerVarName;                // 调用来源类型，非静态为的对象名，静态为类名
    String ownerSimpleName;
    String ownerClassTypeName;
    String ownerSupperClassTypeName;


    // parameter
    String parameterMockStr;            // any(), any() (mock参数使用)
    String parameterDefStr;             // String name, String nick (生成抽象方法实现类使用)
    String parameterClassStr;           // String.class, String.class (反射调用方法时使用)
    String parameterValueStr;           // "20201212", "20201212" (测试调用方法时使用)
    List<String> parameterDescriptorSimpleNames = new ArrayList<>();
    List<String> parameterDescriptorClassNames = new ArrayList<>();
    List<String> parameterSignatureSimpleNames = new ArrayList<>();
    List<String> parameterSignatureClassNames = new ArrayList<>();



    // return
    String returnDescriptorClassName;   // java.util.List
    String returnDescriptorSimpleName;  // List
    String returnSignatureClassName;    // java.util.List<String>
    String returnSignatureSimpleName;   // List<String>
    String returnTypeMockValue;         // thenReturn(""), 可能是变量
    String returnTypeValue;             // "20201212", 仅返回值（用于抽象方法实现时作为返回值）
    String returnName = "result";


    // invoke classify, 用于区分需要使用什么方式的mock存根

    List<GeneMethod> allTestInvokeMethods = new ArrayList<>();

    List<GeneParameter> parameters = new ArrayList<>();     // 方法参数列表，用于作为局部变量mock
    String invokeMethod = "object";      // 调用方式: 对象object、类class、反射reflection、创建new

    List<GeneMethodVariable> variableTypeList = new ArrayList<>();     // 可访问变量表（类成员变量、方法参数、invoke方法返回值）

    // mock存根方法
    List<GeneMethod> allMethods = new ArrayList<>();
    List<GeneMethod> allMockMethods = new ArrayList<>();
    // ------------------------------------------------------------------------------------------------------ Mockito
    // doAnswer(n->null).when(mapper).run(any(), any());          // 有参、无参的public非静态方法
    List<GeneMethod> doAnswerMethods = new ArrayList<>();
    // ------------------------------------------------------------------------------------------------------ PowerMock
    // whenNew().withNoArguments().thenReturn(new User());
    // whenNew().withAnyArguments().thenReturn(new User());
    // whenNew().withArguments(any(Class.class)).thenReturn(new User());
    List<GeneMethod> whenNewMethods = new ArrayList<>();
    // doAnswer(n->null).when(mapper, "run", any(), any());       // 有参、无参的非public方法
    List<GeneMethod> doAnswerPowerMethods = new ArrayList<>();
    // when(ListUtils.class, "run", any()).thenReturn(null);     // 静态有返回值
    List<GeneMethod> whenStaticMethods = new ArrayList<>();
    // doNothing().when(ListUtils.class, "run", any());           // 静态无返回值
    List<GeneMethod> doNothingStaticMethods = new ArrayList<>();
    // suppress(method(IDemoService.class, "run", String.class))  // 父类方法
    List<GeneMethod> suppressMethods = new ArrayList<>();


    /**
     * 单元测试方法对象创建
     */
    public GeneMethod(LoadMethod loadMethod, GeneClass geneClass) {
        this.loadMethod = loadMethod;
        this.modifier = loadMethod.getAccess();
        this.name = loadMethod.getName();
        this.handlerTypes(loadMethod, geneClass);
        this.handlerInvokeMethods(geneClass);
        this.handlerVariables(geneClass);
    }

    private void handlerTypes(ILodeMethod iLodeMethod, GeneClass geneClass){
        DescriptorType descriptorType = new DescriptorType(iLodeMethod.getDescriptor());
        this.parameterDescriptorClassNames = descriptorType.getParameterClassNames();
        this.parameterDescriptorSimpleNames = descriptorType.getParameterSimpleNames();
        this.returnDescriptorClassName = descriptorType.getClassName();
        this.returnDescriptorSimpleName = descriptorType.getSimpleName();
        SignatureType signatureType = new SignatureType(iLodeMethod.getSignature());
        this.parameterSignatureClassNames = signatureType.getParameterClassNames();
        this.parameterSignatureSimpleNames = signatureType.getParameterSimpleNames();
        this.returnSignatureClassName = signatureType.getClassName();
        this.returnSignatureSimpleName = signatureType.getSimpleName();
        // 额外处理文本
        List<String> simpleNames = getParameterSignatureSimpleNames();
        List<LoadParameter> loadParameters = iLodeMethod.getLoadParameters();
        this.parameterMockStr = ParameterUtil.parameterMockStr(parameterDescriptorClassNames);
        this.parameterDefStr = ParameterUtil.parameterDefStr(simpleNames, loadParameters);
        this.parameterClassStr = ParameterUtil.parameterClassStr(parameterDescriptorClassNames);
        this.parameterValueStr = ParameterUtil.parameterValueStr(parameterDescriptorClassNames);
        this.returnTypeValue = MappingUtil.mappingValue(returnDescriptorClassName);
        this.returnTypeMockValue = MappingUtil.mappingCanVar(returnDescriptorSimpleName, geneClass);


    }

    private void handlerInvokeMethods(GeneClass geneClass){
        List<LoadInvokeMethod> loadInvokeMethods = loadMethod.getLoadInvokeMethods();
        for (int i = 0; i < loadInvokeMethods.size(); i++) {
            LoadInvokeMethod invokeMethod = loadInvokeMethods.get(i);
            GeneMethod geneMethod = new GeneMethod(invokeMethod, geneClass);
            geneMethod.setIndex(i);
            if (geneMethod.ownerClassTypeName.equals(ownerClassTypeName)) {
                geneMethod.ownerSupperClassTypeName = ownerSupperClassTypeName;
            }
            allMethods.add(geneMethod);
            // 分类、public、 static、 doAnswer
            int invokeModifier = geneMethod.modifier;
            boolean isConstructMethod = isConstructMethod(geneMethod);
            boolean isReturn = isReturn();
            // new
            if (isConstructMethod) {
                if (!ExcludeUtil.mockProxyClassExclude(geneMethod.getOwnerClassTypeName()))  {
                    whenNewMethods.add(geneMethod);
                }
                continue;
            }
            // super
            if (geneMethod.ownerClassTypeName.equals(ownerSupperClassTypeName)) {
                suppressMethods.add(geneMethod);
                continue;
            }
            allMockMethods.add(geneMethod);
            // 静态
            if (Modifier.isStatic(invokeModifier)) {
                if (isReturn) {
                    whenStaticMethods.add(geneMethod);
                } else {
                    doNothingStaticMethods.add(geneMethod);
                }
                continue;
            }
            // 非静态
            if (Modifier.isPublic(invokeModifier)) {
                doAnswerMethods.add(geneMethod);
            } else {
                doAnswerPowerMethods.add(geneMethod);
            }
        }
    }

    private void handlerVariables(GeneClass geneClass) {
        // 类成员变量
        for (GeneField testField : geneClass.getAllTestFields()) {
            this.addDistinctVariableType(new GeneMethodVariable(testField));
        }
        // 方法参数值
        for (int i = 0; i < parameterDescriptorClassNames.size(); i++) {
            String parameterDescriptorClassName = parameterDescriptorClassNames.get(i);
            if (!ExcludeUtil.mockProxyClassExclude(parameterDescriptorClassName)) {
                String descriptorSimpleName = parameterDescriptorSimpleNames.get(i);
                GeneMethodVariable variable = new GeneMethodVariable();
                variable.descriptorClassName = parameterDescriptorClassName;
                variable.descriptorSimpleName = descriptorSimpleName;
                variable.signatureClassName = parameterSignatureClassNames.get(i);
                variable.signatureSimpleName = parameterSignatureSimpleNames.get(i);
                variable.name = GStrUtil.varName(descriptorSimpleName);
                variable.type = "parameter";
                this.addDistinctVariableType(variable);
            }
        }
        // invoke new object
        for (GeneMethod newObjectMethod : whenNewMethods) {
            String className = newObjectMethod.ownerClassTypeName;
            if (!ExcludeUtil.mockProxyClassExclude(className)) {
                GeneMethodVariable variable = new GeneMethodVariable();
                variable.descriptorClassName = className;
                variable.descriptorSimpleName = newObjectMethod.ownerSimpleName;
                variable.signatureClassName = className;
                variable.signatureSimpleName = newObjectMethod.ownerSimpleName;
                variable.name = newObjectMethod.ownerVarName;
                variable.type = "invoke_new";
                this.addDistinctVariableType(variable);
            }
        }
        // invoke 方法返回值
        for (GeneMethod testInvokeMethod : allTestInvokeMethods) {
            String descriptorClassName = testInvokeMethod.getReturnDescriptorClassName();
            if (!ExcludeUtil.mockProxyClassExclude(descriptorClassName)) {
                String descriptorSimpleName = testInvokeMethod.getReturnDescriptorSimpleName();
                GeneMethodVariable variable = new GeneMethodVariable();
                variable.descriptorClassName = descriptorClassName;
                variable.descriptorSimpleName = descriptorSimpleName;
                variable.signatureClassName = descriptorClassName;
                variable.signatureSimpleName = descriptorSimpleName;
                variable.name = GStrUtil.varName(descriptorSimpleName);
                variable.type = "invoke_return";
                this.addDistinctVariableType(variable);
            }
        }
    }

    // 去重添加访问变量表
    private void addDistinctVariableType(GeneMethodVariable variable){
        for (GeneMethodVariable geneMethodVariable : variableTypeList) {
            if (geneMethodVariable.descriptorClassName.equals(variable.descriptorClassName)) {
                return;
            }
        }
        variableTypeList.add(variable);
    }


    public List<String> getParameterSignatureClassNames(){
        return  !parameterSignatureClassNames.isEmpty() ? parameterSignatureClassNames: parameterDescriptorClassNames;
    }
    public List<String> getParameterSignatureSimpleNames(){
        return  !parameterSignatureSimpleNames.isEmpty() ? parameterSignatureSimpleNames: parameterDescriptorSimpleNames;
    }

    public String getReturnSignatureSimpleName() {
        return returnSignatureSimpleName != null ? returnSignatureSimpleName: returnDescriptorSimpleName;
    }

    public String getReturnSignatureClassName() {
        return returnSignatureClassName != null ? returnSignatureClassName: returnDescriptorClassName;
    }

    private boolean isReturn(){
        return this.returnDescriptorClassName != null && !"void".equals(this.returnDescriptorClassName);
    }

    private boolean isConstructMethod(GeneMethod geneMethod){
        return "<init>".equals(geneMethod.getName());
    }

    public void addVariable(String name ){

    }
    /**
     * 单元测试方法依赖方法对象创建
     */
    public GeneMethod(LoadInvokeMethod loadInvokeMethod, GeneClass geneClass) {
        this.loadInvokeMethod = loadInvokeMethod;
        this.modifier = opcodeToModifier(loadInvokeMethod.getOpcode());
        this.name = loadInvokeMethod.getName();
        this.handlerTypes(loadInvokeMethod, geneClass);

        this.ownerClassTypeName = StrCastUtil.pathToClassName(loadInvokeMethod.getOwner());
        this.ownerSimpleName = GStrUtil.simpleName(ownerClassTypeName);
        this.ownerVarName = GStrUtil.varName(ownerSimpleName);
    }



    private int opcodeToModifier(int opcode){
        switch (opcode){
            case 182: return Modifier.PUBLIC;     // 普通方法
            case 183: return Modifier.PRIVATE;    // 构造、私有、父类（根据owner区分）
            case 184: return Modifier.STATIC;     // 调用静态方法
            case 185: return Modifier.INTERFACE;  // 调用接口方法
        }
        return 0;
    }
}
